package hos.okhttp.utils;

import android.text.TextUtils;
import android.webkit.MimeTypeMap;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URLDecoder;
import java.util.List;

import hos.http.CloseableUtils;
import hos.http.Http;
import hos.http.HttpFileUtils;
import hos.http.HttpStringUtils;
import hos.http.HttpTag;
import hos.http.Progress;
import hos.http.ProgressListener;
import hos.okhttp.exception.OkHttpException;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.Okio;
import okio.Sink;
import okio.Source;

/**
 * <p>Title: OkHttpFileUtils </p>
 * <p>Description:  </p>
 * <p>Company: www.mapuni.com </p>
 *
 * @author : 蔡俊峰
 * @version : 1.0
 * @date : 2024-03-25 20:28
 */
public class OkHttpFileUtils {

    /**
     * 按照以下顺序返回最终的下载文件的名称
     * <p>
     * 1. 指定文件名
     * 2. 响应头文件名
     * 3. 请求URL路径
     * 4. 时间戳
     */
    public static String getFileName(Response response) {
        Request request = response.request();
        HttpTag.DownloadListeners progressListeners = request.tag(HttpTag.DownloadListeners.class);
        String fileName = null;
        boolean isFileNameDecode = true;
        if (progressListeners != null) {
            fileName = progressListeners.fileName;
            isFileNameDecode = progressListeners.isFileNameDecode;
        }
        if (fileName != null && !fileName.isEmpty()) {
            return fileName;
        }
        String disposition = response.header("Content-Disposition");
        if (disposition != null) {
            String fileNameDisposition = HttpStringUtils.substringAfter(disposition, "filename=", "");
            if (!fileNameDisposition.isEmpty()) {
                fileNameDisposition = fileNameDisposition.trim();
            }
            if (!fileNameDisposition.isEmpty()) {
                return fileNameDisposition;
            }
            String fileNameDispositionMore =  HttpStringUtils.substringAfter(disposition, "filename*=", "");
            if (!fileNameDispositionMore.isEmpty()) {
                fileNameDispositionMore = fileNameDispositionMore.trim();
            }
            if (!fileNameDispositionMore.isEmpty()) {
                return fileNameDispositionMore;
            }
        }
        List<String> pathSegments = request.url().pathSegments();
        String pathLast = pathSegments.get(pathSegments.size() - 1);
        fileName =  HttpStringUtils.substringBefore(pathLast, "?");
        if (fileName.isEmpty()) {
            fileName = "unknown_" + System.currentTimeMillis();
        } else if (isFileNameDecode) {
            try {
                return URLDecoder.decode(fileName, "UTF8");
            } catch (UnsupportedEncodingException e) {
                return fileName;
            }
        }
        return fileName;
    }

    public static File parseFile(Response response) throws OkHttpException {
        Request request = response.request();
        HttpTag.DownloadListeners progressListeners = request.tag(HttpTag.DownloadListeners.class);
        String dir = null;
        String fileName = null;
        boolean isTempFile = false;
        boolean isMD5Verify = false;
        if (progressListeners != null) {
            dir = progressListeners.fileDir;
            isTempFile = progressListeners.isTempFile;
            isMD5Verify = progressListeners.isMD5Verify;
        }
        if (dir == null || dir.length() == 0) {
            dir = Http.getContext().getFilesDir().getAbsolutePath();
        }
        File dirFile = new File(dir);
        File file;
        if (dirFile.isDirectory()) {
            fileName = getFileName(response);
            file = new File(dir, fileName);
        } else {
            final String temp = dir;
            dir =  HttpStringUtils.substringBeforeLast(dir, File.separatorChar);
            fileName =  HttpStringUtils.substringAfterLast(temp, File.separatorChar);
            file = dirFile;
        }
        try {
            if (file.exists()) {
                // MD5校验匹配文件
                if (isMD5Verify) {
                    String md5Header = response.header("Content-MD5");
                    if (md5Header != null && TextUtils.equals(HttpFileUtils.md5(file, true), md5Header)) {
                        if (progressListeners != null && !progressListeners.isEmpty()) {
                            Progress progress = new Progress();
                            long fileSize = file.length();
                            progress.currentByteCount = fileSize;
                            progress.totalByteCount = fileSize;
                            progress.intervalByteCount = fileSize;
                            progress.finish = true;
                            for (ProgressListener progressListener : progressListeners) {
                                progressListener.onProgress(progress);
                            }
                        }
                        return file;
                    }
                }
                // 命名冲突添加序列数字的后缀
                if (TextUtils.equals(file.getName(), fileName)) {
                    String fileExtension =  HttpStringUtils.substringAfterLast(file.getName(), '.', "");
                    String fileNameWithoutExtension =  HttpStringUtils.substringBeforeLast(file.getName(), '.');
                    file = HttpFileUtils.rename(dir, fileNameWithoutExtension, fileExtension, 1);
                }
            }

            // 临时文件
            if (isTempFile) {
                file = new File(dir, file.getName() + ".okhttp-download");
            }
            ResponseBody responseBody = response.body();
            if (responseBody == null) {
                return null;
            }
            BufferedSource source = null;
            try {
                source = responseBody.source();
                if (!file.exists()) file.createNewFile();
                Sink sink = Okio.sink(new FileOutputStream(file, false));
                BufferedSink buffer = Okio.buffer(sink);
                buffer.writeAll(source);
            } finally {
                CloseableUtils.closeFinally(source);
            }
            // 下载完毕删除临时文件
            if (isTempFile) {
                File fileFinal = new File(dir, fileName);
                file.renameTo(fileFinal);
                return fileFinal;
            }
            return file;
        } catch (SocketException e) {
            // 取消请求需要删除下载临时文件
            if (isTempFile) file.delete();
            throw OkHttpException.throwCancel(response.request(), e);
        } catch (Exception e) {
            throw OkHttpException.throwDownload(response.request(), e);
        }
    }

    public static MediaType mediaType(File file) {
        String fileExtension = MimeTypeMap.getFileExtensionFromUrl(file.getAbsolutePath());
        String mimeTypeFromExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
        if (mimeTypeFromExtension == null) {
            return null;
        }
        return MediaType.get(mimeTypeFromExtension);
    }

    public static RequestBody toRequestBody(File file, MediaType contentType) {
        return new RequestBody() {
            @Nullable
            @Override
            public MediaType contentType() {
                return contentType != null ? contentType : mediaType(file);
            }

            @Override
            public long contentLength() throws IOException {
                return file.length();
            }

            @Override
            public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException {
                Source source = null;
                try {
                    source = Okio.source(file);
                    bufferedSink.writeAll(source);
                } finally {
                    CloseableUtils.closeFinally(source);
                }
            }
        };
    }
}
